from . import passport_blue
from flask import request, abort, make_response, jsonify, session
from technews.utils.captcha.captcha import captcha
from technews import redis_store, constants, response_code, db
import logging, re, random, datetime
from technews.libs.yuntongxun.sms import CCP
from technews.models import User


@passport_blue.route('/logout', methods=["GET"])
def logout():
    """
    退出登录
    1.只需要将状态保持信息删除即可
    2.返回退出登录的结果
    :return:
    """
    # 1.只需要将状态保持信息删除即可
    session.pop("user_id", None)
    session.pop("nick_name", None)
    session.pop("mobile", None)

    # 2.返回退出登录的结果
    return jsonify(errno=response_code.RET.OK, errmsg="退出登录成功")


# def passwordhash(password):
#     from werkzeug.security import generate_password_hash
#     password_hash = generate_password_hash(password)
#     return password_hash


@passport_blue.route("/login", methods=["POST"])
def login():
    """
    登录：本质就是查询数据
    1.接受参数：mobile,password(明文)
    2.校验参数：参数是否齐全，校验手机号码是否正确
    3.使用mobile查询user是否存在，校验手机号码是否存在(用户是否存在)
    4.如果user存在，再去校验密码
    5.如果密码校验通过，就记录最后一次登录的时间和状态保持
    6.响应登录结果
    :return: 登录成功
    """
    # 1.接受参数：mobile,password(明文)
    json_dict = request.json
    mobile = json_dict.get("mobile")
    password_client = json_dict.get("password")

    # 2.校验参数：参数是否齐全，校验手机号码是否正确
    if not all([mobile, password_client]):
        return jsonify(errno=response_code.RET.PARAMERR, errmsg="缺少必传参数")
    if not re.match("^1[345678][0-9]{9}$", mobile):
        return jsonify(errno=response_code.RET.PARAMERR, errmsg='手机号码格式错误')

    # 3.使用mobile查询user是否存在，校验手机号码是否存在(用户是否存在)
    try:
        user = User.query.filter(User.mobile == mobile).first()
    except Exception as e:
        logging.error(e)
        return jsonify(errno=response_code.RET.DBERR, errmsg='查询用户名或密码错误')
    if not user:
        return jsonify(errno=response_code.RET.NODATA, errmsg='用户名或密码错误')

    # 4.如果user存在，再去校验密码
    if not user.check_password(password_client):
        return jsonify(errno=response_code.RET.PARAMERR, errmsg='用户名或密码错误')

    # 5.如果密码校验通过，就记录最后一次登录的时间和状态保持
    user.last_login = datetime.datetime.now()
    try:
        db.session.commit() # 修改记录不需要add
    except Exception as e:
        logging.error(e)
        db.session.rollback()
        return jsonify(errno=response_code.RET.DATAERR, errmsg='登录失败')

    # 状态保持
    session["user_id"] = user.id
    session["nick_name"] = user.nick_name
    session["mobile"] = user.mobile

    # 6.响应登录结果
    return jsonify(errno=response_code.RET.OK, errmsg='登录成功')


@passport_blue.route("/register", methods=["POST"])
def register():
    """
    注册：本质就是存数据
    1.接受参数：mobile,smscode,password
    2.教研参数：参数是否齐全，校验手机号码格式是否正确
    3.使用mobile读取redis中的短信验证码
    4.使用客户端传入的短信验证码和服务器短信验证码进行对比
    5.创建User模型对象，并给用户属性赋值
    6.记得保存最后一次登录的时间(last_login,为了实现注册即登录)
    7.需要将用户注册的数据保存到mysql数据库
    8.将状态保持的信息写入到session(为了实现注册即登录)
    9.返回注册结果
    """
    # 1.接受参数：mobile,smscode,password
    json_dict = request.json
    mobile = json_dict.get("mobile")
    sms_code_client = json_dict.get("smscode")
    password = json_dict.get("password")

    # 2.校验参数：参数是否齐全，校验手机号码格式是否正确
    if not all([mobile, sms_code_client, password]):
        return jsonify(errno=response_code.RET.PARAMERR, errmsg="缺少必传参数")
    if not re.match("^1[345678][0-9]{9}$", mobile):
        return jsonify(errno=response_code.RET.PARAMERR, errmsg='手机号码格式错误')

    # 3.使用mobile读取短信验证码
    try:
        sms_code_server = redis_store.get('SMS:' + mobile)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=response_code.RET.DBERR, errmsg='查询验证码失败')
    if not sms_code_server:
        return jsonify(errno=response_code.RET.NODATA, errmsg='短信验证码不存在')

    # 4.使用客户端传入的短信验证码和服务器短信验证码进行对比
    if sms_code_client != sms_code_server:
        return jsonify(errno=response_code.RET.PARAMERR, errmsg='短信验证码输入有误')

    # 5.创建User模型对象，并给用户属性赋值
    user = User()
    user.nick_name = mobile  # 手机号默认就是用户名
    user.mobile = mobile

    # from werkzeug.security import generate_password_hash
    # password_hash = generate_password_hash(password)
    # user.password_hash = passwordhash(password)  # password是明文

    # password:是即将给User模型类增加的属性
    # 在password属性的setter方法中，拿着明文密码进行加密处理，再保存到password_hash
    user.password = password

    # 6.记得保存最后一次登录的时间(last_login,为了实现注册即登录)
    user.last_login = datetime.datetime.now()  # 最后一次登录时间(实现注册即登录)

    # 7.需要将用户注册的数据保存到mysql数据库
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        logging.error(e)
        db.session.rollback()
        return jsonify(errno=response_code.RET.DATAERR, errmsg='存储用户数据失败')

    # 8.将状态保持的信息写入到session(为了实现注册即登录)
    session["user_id"] = user.id

    # 9.返回注册结果
    return jsonify(errno=response_code.RET.OK, errmsg='注册成功')


@passport_blue.route('/sms_code', methods=['POST'])
def send_sms_code():
    """发送短信验证码
    1.接受参数：monile，image_code, image_code_id
    2.校验参数：判断参数是否齐全，判断手机号码格式是否正确
    3.使用image_code_id从reidis中读取服务器存储的图片验证码
    4.对比服务器存储的图片验证码信息
    5.生成短信验证码，长度是6位的数字
    6.如果对比成功，就使用CCP()单例发送短信验证码
    7.发送短信的验证码成功，就把短信验证码存储到redis()
    8.返回短信验证码发送的结果
    """
    # 1.接受参数：monile，image_code, image_code_id
    json_dict = request.json
    mobile = json_dict.get('mobile')
    image_code_client = json_dict.get('image_code')
    image_code_id = json_dict.get('image_code_id')

    # 2.校验参数：判断参数是否齐全，判断手机号码格式是否正确
    if not all([mobile, image_code_client, image_code_id]):
        return jsonify(errno=response_code.RET.PARAMERR, errmsg='缺少参数')
    if not re.match("^1[345678][0-9]{9}$", mobile):
        return jsonify(errno=response_code.RET.PARAMERR, errmsg='手机号码格式错误')

    # 3.使用image_code_id从reidis中读取服务器存储的图片验证码
    try:
        image_code_server = redis_store.get("ImageCode:" + image_code_id)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=response_code.RET.DBERR, errmsg='查询图片验证码失败')
    # 判断image_code_server是否存在
    if not image_code_server:
        return jsonify(errno=response_code.RET.NODATA, errmsg='图片验证码不存在')

    # 4.对比服务器存储的图片验证码信息
    if image_code_client.lower() != image_code_server.lower():
        return jsonify(errno=response_code.RET.PARAMERR, errmsg='图片验证码输入错误')

    # 5.生成短信验证码，长度是6位的数字,不够6位补0
    sms_code = "%06d" % random.randint(0, 999999)
    logging.debug(sms_code)

    # 6.如果对比成功，就使用CCP()单例发送短信验证码
    result = CCP().send_sms_code(mobile, [sms_code, 5], 1)
    if result != 0:
        return jsonify(errno=response_code.RET.THIRDERR, errmsg='发送短信失败')

    # 7.发送短信的验证码成功，就把短信验证码存储到redis()
    try:
        redis_store.set('SMS:' + mobile, sms_code, constants.SMS_CODE_REDIS_EXPIRES)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=response_code.RET.DATAERR, errmsg='存储信息失败')

    # 8.返回短信验证码发送的结果
    return jsonify(errno=response_code.RET.OK, errmsg='发送短信成功')


@passport_blue.route('/image_code')
def get_image_code():
    """
    提供图片验证码
    1.接受请求：imageCodeId (UUID,图片验证码唯一标识符)
    2.校验参数：必须传入imageCodeId
    3.生成图片验证码的文字和图片信息
    4.将imageCodeId和图片验证码文字绑定到redis
    5.响应图片验证码的图片信息给用户
    :return:
    """
    # 1.接受请求：imageCodeId (UUID,图片验证码唯一标识符)
    image_code_id = request.args.get('imageCodeId')

    # 2.校验参数：必须传入imageCodeId
    if not image_code_id:
        abort(400)  # 缺少参数

    # 3.生成图片验证码的文字和图片信息:text:图片验证码的文字,image：图片验证码图片
    name, text, image = captcha.generate_captcha()

    # 4.将imageCodeId和图片验证码文字绑定到redis
    try:
        redis_store.set('ImageCode:' + image_code_id, text, constants.IMAGE_CODE_REDIS_EXPIRES)
    except Exception as e:
        logging.error(e)
        abort(500)  # 表示服务器错误

    # 5.响应图片验证码的图片信息给用户
    response = make_response(image)  # 将image作为响应体
    # 指定响应体数据的类型
    response.headers['Content-Type'] = 'image/jpg'
    return response


